package org.smarthings.things.management.impl;

import java.math.BigInteger;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Map;

import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.smarthings.things.management.Thing;
import org.smarthings.things.management.ThingAction;
import org.smarthings.things.management.ThingProperty;
import org.smarthings.things.management.event.ThingEventConstants;
import org.smarthings.things.management.metadata.ThingDataType;

public class ThingsEventPropagator {
	private EventAdmin eventAdmin;

	public ThingsEventPropagator(EventAdmin eventAdmin) {
		this.eventAdmin = eventAdmin;
	}

	public void fireThingRegisteredEvent(Thing thing) {
		Dictionary<String, Object> properties = new Hashtable<String, Object>(2);
		properties.put(ThingEventConstants.PROPERTY_THING, getThing(thing));
		properties.put(ThingEventConstants.PROPERTY_TIMESTAMP, System.currentTimeMillis());
		Event event = new Event(ThingEventConstants.TOPIC_THING_REGISTERED, properties);
		if (eventAdmin != null) {
			eventAdmin.postEvent(event);
		}
	}

	public void fireThingUnregisteredEvent(Thing thing) {
		Dictionary<String, Object> properties = new Hashtable<String, Object>(2);
		properties.put(ThingEventConstants.PROPERTY_THING, getThing(thing));
		properties.put(ThingEventConstants.PROPERTY_TIMESTAMP, System.currentTimeMillis());
		Event event = new Event(ThingEventConstants.TOPIC_THING_UNREGISTERED, getThing(thing));
		if (eventAdmin != null) {
			eventAdmin.postEvent(event);
		}
	}

	public void fireThingStateChangedEvent(Thing thing) {
		Dictionary<String, Object> properties = new Hashtable<String, Object>(2);
		properties.put(ThingEventConstants.PROPERTY_THING, getThing(thing));
		properties.put(ThingEventConstants.PROPERTY_TIMESTAMP, System.currentTimeMillis());
		Event event = new Event(ThingEventConstants.TOPIC_THING_STATE_CHANGED, properties);
		if (eventAdmin != null) {
			eventAdmin.postEvent(event);
		}
	}

	public void fireThingPropertyChangedEvent(Thing thing, ThingProperty property) {
		Dictionary<String, Object> properties = new Hashtable<String, Object>(2);
		properties.put(ThingEventConstants.PROPERTY_THING, getThing(thing));
		properties.put(ThingEventConstants.PROPERTY_TIMESTAMP, System.currentTimeMillis());
		properties.put(ThingEventConstants.PROPERTY_THING_CHANGED_PROPERTY,
				getThingProperty(property));
		Event event = new Event(ThingEventConstants.TOPIC_THING_PROPERTY_CHNAGED, properties);
		if (eventAdmin != null) {
			eventAdmin.postEvent(event);
		}
	}

	public void fireThingActionExecutionEvent(Thing thing, ThingAction action,
			Map<String, Object> arguments) {
		Dictionary<String, Object> properties = new Hashtable<String, Object>(2);
		properties.put(ThingEventConstants.PROPERTY_THING, getThing(thing));
		properties.put(ThingEventConstants.PROPERTY_TIMESTAMP, System.currentTimeMillis());
		properties.put(ThingEventConstants.PROPERTY_THING_EXECUTED_ACTION,
				getThingAction(action, arguments));
		Event event = new Event(ThingEventConstants.TOPIC_THING_ACTION_EXECUTED, properties);
		if (eventAdmin != null) {
			eventAdmin.postEvent(event);
		}
	}

	private Dictionary<String, Object> getThing(Thing thing) {
		Dictionary<String, Object> properties = new Hashtable<String, Object>();
		properties.put(ThingEventConstants.PROPERTY_THING_UUID, thing.getUUID());
		properties.put(ThingEventConstants.PROPERTY_THING_STATE, thing.getState());
		if (thing.getName() != null) {
			properties.put(ThingEventConstants.PROPERTY_THING_NAME, thing.getName());
		}
		if (thing.getType() != null) {
			properties.put(ThingEventConstants.PROPERTY_THING_TYPE, thing.getType());
		}
//		if (thing.getTags() != null) {
//			properties.put(ThingEventConstants.PROPERTY_THING_TAGS, thing.getTags());
//		}
		return properties;
	}

	private Dictionary<String, Object> getThingProperty(ThingProperty property) {
		Dictionary<String, Object> properties = new Hashtable<String, Object>();
		properties.put(ThingEventConstants.PROPERTY_THING_CHANGED_PROPERTY_UUID, //
				property.getUUID());
		properties.put(ThingEventConstants.PROPERTY_THING_CHANGED_PROPERTY_TYPE, //
				property.getType());

		Object propertyValue = property.getValue();
		if (propertyValue != null) {
			// ClassLoader loader = Thread.currentThread().getContextClassLoader();
			Class propertyValueType = String.class;
			if (property.getMetadata() != null) {
				ThingDataType type = property.getMetadata().getType();
				if (type != null) {
					propertyValueType = type.javaType();
				}
				// String type = property.getMetadata().getType();
				// if (type != null) {
				// try {
				// propertyValueType = loader.loadClass(type);
				// } catch (ClassNotFoundException e) {
				// // log debug
				// }
				// }
			}

			if (String.class.equals(propertyValueType)) {
				properties.put(ThingEventConstants.PROPERTY_THING_CHANGED_PROPERTY_VALUE_PREFIX
						+ ThingEventConstants.PROPERTY_TYPE_STRING, propertyValue);
			} else if (Boolean.class.equals(propertyValueType)) {
				properties.put(ThingEventConstants.PROPERTY_THING_CHANGED_PROPERTY_VALUE_PREFIX
						+ ThingEventConstants.PROPERTY_TYPE_BOOLEAN, propertyValue);
			} else if (Integer.class.equals(propertyValueType)) {
				properties.put(ThingEventConstants.PROPERTY_THING_CHANGED_PROPERTY_VALUE_PREFIX
						+ ThingEventConstants.PROPERTY_TYPE_INTEGER, propertyValue);
			} else if (Long.class.equals(propertyValueType)) {
				properties.put(ThingEventConstants.PROPERTY_THING_CHANGED_PROPERTY_VALUE_PREFIX
						+ ThingEventConstants.PROPERTY_TYPE_LONG, propertyValue);
			} else if (Float.class.equals(propertyValueType)) {
				properties.put(ThingEventConstants.PROPERTY_THING_CHANGED_PROPERTY_VALUE_PREFIX
						+ ThingEventConstants.PROPERTY_TYPE_FLOAT, propertyValue);
			} else if (Double.class.equals(propertyValueType)) {
				properties.put(ThingEventConstants.PROPERTY_THING_CHANGED_PROPERTY_VALUE_PREFIX
						+ ThingEventConstants.PROPERTY_TYPE_DOUBLE, propertyValue);
			} else if (BigInteger.class.equals(propertyValueType)) {
				properties.put(ThingEventConstants.PROPERTY_THING_CHANGED_PROPERTY_VALUE_PREFIX
						+ ThingEventConstants.PROPERTY_TYPE_BIG_INTEGER, propertyValue);
			}
		}
		return properties;
	}

	private Dictionary<String, Object> getThingAction(ThingAction action,
			Map<String, Object> arguments) {
		Dictionary<String, Object> properties = new Hashtable<String, Object>();
		properties.put(ThingEventConstants.PROPERTY_THING_EXECUTED_ACTION_UUID, action.getUUID());
		properties.put(ThingEventConstants.PROPERTY_THING_EXECUTED_ACTION_TYPE, action.getType());
		if (arguments != null && arguments.size() > 0) {
			properties.put(ThingEventConstants.PROPERTY_THING_EXECUTED_ACTION_ARGUMENTS, arguments);
		}
		return properties;

	}

}
